package com.suredy.app.porject.ctrl;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.suredy.app.porject.constants.ContractOpLogType;
import com.suredy.app.porject.constants.ContractStatus;
import com.suredy.app.porject.constants.ContractType;
import com.suredy.app.porject.constants.FlowStatus;
import com.suredy.app.porject.constants.UserFileType;
import com.suredy.app.porject.constants.WorkPlanMilestone;
import com.suredy.app.porject.ctrl.flow.FlowCtrl;
import com.suredy.app.porject.ctrl.flow.FlowCtrlForProject;
import com.suredy.app.porject.ctrl.view.ViewRoleCtrlForContract;
import com.suredy.app.porject.formdata.FormDataForContractStart;
import com.suredy.app.porject.model.Bid;
import com.suredy.app.porject.model.Biddinger;
import com.suredy.app.porject.model.Contract;
import com.suredy.app.porject.model.ContractFile;
import com.suredy.app.porject.model.ContractMoney;
import com.suredy.app.porject.model.ContractOpLog;
import com.suredy.app.porject.model.ContractPayinPlan;
import com.suredy.app.porject.model.ContractPayoutPlan;
import com.suredy.app.porject.model.ContractWorkPlan;
import com.suredy.app.porject.model.FlowOfContract2User;
import com.suredy.app.porject.model.embedded.BiddingerInfo;
import com.suredy.app.porject.model.vo.ContractPayinPlanArray;
import com.suredy.app.porject.model.vo.ContractPayoutPlanArray;
import com.suredy.app.porject.model.vo.ContractWorkPlanArray;
import com.suredy.app.porject.model.vo.MainContractEditModel;
import com.suredy.app.porject.roles.SystemAtomRoles;
import com.suredy.app.porject.service.BidSrv;
import com.suredy.app.porject.service.BiddingerSrv;
import com.suredy.app.porject.service.ContractFileSrv;
import com.suredy.app.porject.service.ContractMoneySrv;
import com.suredy.app.porject.service.ContractOpLogSrv;
import com.suredy.app.porject.service.ContractPayinPlanSrv;
import com.suredy.app.porject.service.ContractPayoutPlanSrv;
import com.suredy.app.porject.service.ContractSrv;
import com.suredy.app.porject.service.ContractWorkPlanSrv;
import com.suredy.app.porject.service.FlowOfContract2UserSrv;
import com.suredy.app.porject.service.ProjectCompany;
import com.suredy.app.porject.service.ProjectConstants;
import com.suredy.app.porject.tools.FileTool;
import com.suredy.core.helper.ApplicationContextHelper;
import com.suredy.core.mvc.model.MessageModel;
import com.suredy.security.entity.OrgEntity;
import com.suredy.security.entity.UserEntity;

/**
 * 合同控制器
 * 
 * @author VIVID.G
 * @since 2015-10-15
 * @version v0.1
 */
@Controller
@RequestMapping("/contract")
public class ContractCtrl extends FlowCtrl {

	private final static Logger log = LoggerFactory.getLogger(ContractCtrl.class);

	@Autowired
	private FileTool fileTool;

	@Autowired
	private BidSrv bidSrv;

	@Autowired
	private ContractSrv contractSrv;

	@Autowired
	private ContractFileSrv contractFileSrv;

	@Autowired
	private ContractMoneySrv contractMoneySrv;

	@Autowired
	private ProjectConstants constants;

	@Autowired
	private ProjectCompany company;

	@Autowired
	private ContractOpLogSrv contractOpLogSrv;

	@Autowired
	private ContractWorkPlanSrv contractWorkPlanSrv;

	@Autowired
	private ContractPayinPlanSrv contractPayinPlanSrv;

	@Autowired
	private ContractPayoutPlanSrv contractPayoutPlanSrv;

	@Autowired
	private BiddingerSrv biddingerSrv;

	@RequestMapping(value = "/create-by-bid", method = RequestMethod.GET)
	public String createByBid(String bidId, Model model) {
		String view = "app/contract/contract-create-by-bid";

		if (StringUtils.isBlank(bidId))
			return view;

		Bid bid = this.bidSrv.get(bidId);

		if (bid == null)
			return view;

		// 可能是没有招标，直接签订合同
		Contract data = this.newContract(bid, ContractType.MAIN);

		model.addAttribute("data", data);
		model.addAttribute("jiesuanWay", this.constants.getContractJiesuanWay(null));

		return view;
	}

	@RequestMapping(value = "/create-by-bid", method = RequestMethod.POST)
	@ResponseBody
	public Object createByBid(Contract contract, @RequestParam(value = "contractFile", required = true) MultipartFile contractFile, @RequestParam(value = "file", required = false) MultipartFile[] file) {
		if (contract == null)
			return new MessageModel(false, "无效的合同信息！", null);

		if (contract.getBid() == null || StringUtils.isBlank(contract.getBid().getId()))
			return new MessageModel(false, "无效的招标信息！", null);

		// 修复费用
		if (contract.getMoney() != null) {
			for (Iterator<ContractMoney> it = contract.getMoney().iterator(); it.hasNext();) {
				ContractMoney m = it.next();

				// 没有填写金额与时间的项不进行保存操作
				if (m == null || (m.getTotal() == null && m.getTime() == null)) {
					it.remove();
					continue;
				}

				// 设置父对象
				m.setContract(contract);
			}
		}

		// 初始化流程
		this.createContractFlowInfo(contract, new UserEntity(this.getUser().getId()), FlowStatus.FS2000_CONTRACT_TO_CREATE);

		contract.setFlowStatus(FlowStatus.FS2000_CONTRACT_TO_CREATE);

		Contract data = this.contractSrv.save(contract);

		if (data == null || StringUtils.isBlank(data.getId()))
			return new MessageModel(false, "保存合同信息失败！", null);

		// 修改招标状态为completed
		FlowCtrlForProject fcfp = ApplicationContextHelper.getBeanByType(FlowCtrlForProject.class);
		fcfp.changeStatusForBid(data.getBid().getId(), FlowStatus.FS1002_BID_COMPLETED);

		// 合同文件
		if (contractFile != null)
			this.saveFile(contractFile, data.getId(), UserFileType.F200_CONTRACT_FILE);

		// 保存文件
		if (file != null) {
			for (MultipartFile f : file) {
				this.saveFile(f, data.getId(), UserFileType.Z900_OTHER);
			}
		}

		return new MessageModel(true, "操作成功！", data);
	}

	@RequestMapping(value = "/create/{type}", method = RequestMethod.GET)
	public String createContract(@PathVariable int type, Model model) {
		String view = "app/contract/contract-create";

		ContractType t = null;

		switch (type) {
			case 0:
				t = ContractType.A_CONTRACT;
				break;
			default:
				t = ContractType.B_CONTRACT;
		}

		model.addAttribute("type", t);
		model.addAttribute("jiesuanWay", this.constants.getContractJiesuanWay(null));
		model.addAttribute("biddingers", this.getBiddingers());

		List<String> ownCompany = this.company.getAllCompany();
		if (ownCompany == null || ownCompany.isEmpty()) {
			ownCompany = new ArrayList<String>();
			ownCompany.add(this.getUser().getOrgName());
		}

		model.addAttribute("company", ownCompany);

		return view;
	}

	@RequestMapping(value = "/create", method = RequestMethod.POST)
	@ResponseBody
	public Object createContract(Contract contract, @RequestParam(value = "contractFile", required = true) MultipartFile contractFile, @RequestParam(value = "file", required = false) MultipartFile[] file) {
		if (contract == null)
			return new MessageModel(false, "操作失败，无效的合同信息！", null);

		// 修复费用
		if (contract.getMoney() != null) {
			for (Iterator<ContractMoney> it = contract.getMoney().iterator(); it.hasNext();) {
				ContractMoney m = it.next();

				// 没有填写金额与时间的项不进行保存操作
				if (m == null || (m.getTotal() == null && m.getTime() == null)) {
					it.remove();
					continue;
				}

				// 设置父对象
				m.setContract(contract);
			}
		}

		// 初始化流程
		this.createContractFlowInfo(contract, new UserEntity(this.getUser().getId()), FlowStatus.FS2000_CONTRACT_TO_CREATE);

		contract.setFlowStatus(FlowStatus.FS2000_CONTRACT_TO_CREATE);

		Contract data = this.contractSrv.save(contract);

		if (data == null || StringUtils.isBlank(data.getId()))
			return new MessageModel(false, "保存合同信息失败！", null);

		// 合同文件
		if (contractFile != null)
			this.saveFile(contractFile, data.getId(), UserFileType.F200_CONTRACT_FILE);

		// 保存文件
		if (file != null) {
			for (MultipartFile f : file) {
				this.saveFile(f, data.getId(), UserFileType.Z900_OTHER);
			}
		}

		return new MessageModel(true, "操作成功！", data.getId());
	}

	@RequestMapping(value = "/list/{status}", method = RequestMethod.GET)
	public String list(@PathVariable ContractStatus status, Integer page, Integer pageSize, Model model) {
		String view = "app/contract/contract-list";

		if (status == null)
			return view;

		// 只显示主合同
		Contract search = new Contract();
		search.setStatus(status);

		DetachedCriteria dc = this.contractSrv.getDc(search);

		dc.add(Restrictions.in("type", new ContractType[] {ContractType.MAIN, ContractType.A_CONTRACT, ContractType.B_CONTRACT}));

		if (page == null || page <= 0)
			page = 1;

		if (pageSize == null || pageSize <= 0)
			pageSize = 30;

		// List<Contract> data = this.contractSrv.readByEntity(search);
		@SuppressWarnings("unchecked")
		List<Contract> data = (List<Contract>) this.contractSrv.readByCriteria(dc, page, pageSize);

		if (data == null || data.isEmpty())
			return view;

		model.addAttribute("data", data);
		model.addAttribute("status", status);
		model.addAttribute("page", page);
		model.addAttribute("pageSize", pageSize);
		model.addAttribute("count", this.contractSrv.getCountByCriteria(dc));

		return view;
	}

	@RequestMapping(value = "/view/{id}", method = RequestMethod.GET)
	public String view(@PathVariable String id, Model model) {
		String view = "app/contract/contract-view";

		if (StringUtils.isBlank(id))
			return view;

		Contract data = this.contractSrv.get(id);

		if (data == null)
			return view;

		// 甲方合同
		if (ContractType.A_CONTRACT.equals(data.getType()))
			view = "app/contract/contract-view-for-a";

		model.addAttribute("data", data);
		model.addAttribute("roles", new ViewRoleCtrlForContract(data, this.getUser(), this.getRoles()));

		return view;
	}

	@RequestMapping(value = "/view/{id}/{tab}", method = RequestMethod.GET)
	public String view(@PathVariable String id, @PathVariable String tab, Model model) {
		model.addAttribute("tab", tab);

		return this.view(id, model);
	}

	@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
	public String editForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-edit";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		Contract data = this.contractSrv.get(id);

		if (data == null) {
			model.addAttribute("notfound", true);
			return view;
		}

		// 编辑人员必须为创建人且处于活动阶段
		if (data.getFlowStatus() == null || !data.getFlowStatus().equals(FlowStatus.FS2000_CONTRACT_TO_CREATE)) {
			model.addAttribute("data", data);
			model.addAttribute("cannotedit", true);
			return view;
		}

		List<String> ownCompany = this.company.getAllCompany();
		if (ownCompany == null || ownCompany.isEmpty()) {
			ownCompany = new ArrayList<String>();
			ownCompany.add(this.getUser().getOrgName());
		}

		switch (data.getType()) {
			case MAIN:
				view = "app/contract/contract-edit-from-bid";

				model.addAttribute("jiesuanWay", this.constants.getContractJiesuanWay(null));
				model.addAttribute("biddingers", this.getBiddingers());
				model.addAttribute("company", ownCompany);
				model.addAttribute("data", data);

				break;
			case A_CONTRACT:
			case B_CONTRACT:
				view = "app/contract/contract-edit";

				model.addAttribute("jiesuanWay", this.constants.getContractJiesuanWay(null));
				model.addAttribute("biddingers", this.getBiddingers());
				model.addAttribute("company", ownCompany);
				model.addAttribute("data", data);

				break;
			default:
				// 不是主合同，直接返回错误
				model.addAttribute("data", data);
				model.addAttribute("errortype", true);
				return view;
		}

		return view;
	}

	@RequestMapping(value = "/edit", method = RequestMethod.POST)
	@ResponseBody
	public Object editForm(MainContractEditModel contract) {
		if (contract == null || StringUtils.isBlank(contract.getId()))
			return new MessageModel(false, "无效的合同信息！", null);

		Contract data = this.contractSrv.get(contract.getId());

		if (data == null || StringUtils.isBlank(data.getId()))
			return new MessageModel(false, "保存合同信息失败！", null);

		try {
			PropertyUtils.copyProperties(data, contract);
		} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
			return new MessageModel(false, "保存合同信息失败！\n" + e.getMessage(), null);
		}

		data = this.contractSrv.update(data);

		if (data == null || StringUtils.isBlank(data.getId()))
			return new MessageModel(false, "保存合同信息失败！", null);

		// 记录日志
		this.recordOpLog(data, ContractOpLogType.CONTRACT_EDIT, null);

		return new MessageModel(true, "操作成功！", data.getId());
	}

	@RequestMapping(value = "/payout/{contractId}/{bidId}", method = RequestMethod.GET)
	public String payoutForm(@PathVariable String contractId, @PathVariable String bidId, Model model) {
		String view = "app/contract/contract-payout";

		if (StringUtils.isBlank(contractId)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("bidId", bidId);
		model.addAttribute("contractId", contractId);
		model.addAttribute("payoutNames", this.constants.getContractPayoutNames(null));
		model.addAttribute("payoutNameDicKey", ProjectConstants.DK_CONTRACT_PAYOUT_NAME);

		return view;
	}

	@RequestMapping(value = "/add-payout", method = RequestMethod.POST)
	@ResponseBody
	public Object addPayout(ContractMoney money) {
		if (money == null || money.getContract() == null || StringUtils.isBlank(money.getContract().getId()))
			return new MessageModel(false, "无效的费用信息！", null);

		if (StringUtils.isBlank(money.getName()) || money.getTotal() == null || money.getTime() == null)
			return new MessageModel(false, "[资金名目]、[支出金额]和[支出日期]不能为空！", null);

		money.setPayOut(true);

		money = this.contractMoneySrv.save(money);

		if (money == null || StringUtils.isBlank(money.getId()))
			return new MessageModel(false, "保存费用信息失败！", null);

		return new MessageModel(true, "操作成功！", money);
	}

	@RequestMapping(value = "/upload-contract-file/{contractId}", method = RequestMethod.GET)
	public String uploadContractFileForm(@PathVariable String contractId, Model model) {
		String view = "app/contract/contract-file-upload";

		if (StringUtils.isBlank(contractId)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("contractId", contractId);

		return view;
	}

	@RequestMapping(value = "/upload-contract-file/{contractId}", method = RequestMethod.POST)
	@ResponseBody
	public Object uploadContractFile(@PathVariable String contractId, @RequestParam(value = "file", required = false) MultipartFile file) {
		if (file == null)
			return new MessageModel(false, "请选择需要上传的文件！", null);

		if (StringUtils.isBlank(contractId))
			return new MessageModel(false, "无效的合同信息！", null);

		if (!this.saveFile(file, contractId, UserFileType.F200_CONTRACT_FILE))
			return new MessageModel(false, "上传文件失败！", null);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/common-file-upload/{contractId}/{fileType}", method = RequestMethod.POST)
	public String commonFileUploadForm(@PathVariable String contractId, @PathVariable UserFileType fileType, String suffix, Model model) {
		String view = "app/contract/common-file-upload";

		if (StringUtils.isBlank(contractId) || fileType == null) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("bidId", contractId);
		model.addAttribute("fileType", fileType);
		model.addAttribute("suffix", suffix);

		return view;
	}

	@RequestMapping(value = "/common-file-upload", method = RequestMethod.POST)
	@ResponseBody
	public Object commonFileUpload(String contractId, UserFileType fileType, @RequestParam(value = "file", required = false) MultipartFile file) {
		if (file == null)
			return new MessageModel(false, "请选择需要上传的文件！", null);

		if (StringUtils.isBlank(contractId))
			return new MessageModel(false, "无效的合同信息！", null);

		if (fileType == null)
			return new MessageModel(false, "无法识别文件类型！", null);

		if (!this.saveFile(file, contractId, fileType))
			return new MessageModel(false, "文件保存失败！", null);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/start/{id}", method = RequestMethod.GET)
	public String startContractForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-start";

		// 无效的合同信息
		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("id", id);
		model.addAttribute("xmh", this.getUserByRole("" + SystemAtomRoles.HEADER_XIANGMU));
		model.addAttribute("jjh", this.getUserByRole("" + SystemAtomRoles.HEADER_JIJING));

		return view;
	}

	@RequestMapping(value = "/start", method = RequestMethod.POST)
	@ResponseBody
	public Object startContract(FormDataForContractStart data) {
		if (StringUtils.isBlank(data.getId()))
			return new MessageModel(false, "合同信息无效！", null);

		if (StringUtils.isBlank(data.getXiangmuHeader()))
			return new MessageModel(false, "[项目负责人]必须填写！", null);

		if (StringUtils.isBlank(data.getJijingHeader()))
			return new MessageModel(false, "[技经负责人]必须填写！", null);

		Contract c = this.contractSrv.get(data.getId());

		if (c == null)
			return new MessageModel(false, "未能找到对应的合同信息！", null);

		c.setStatus(ContractStatus.START);
		c.setXiangmuHeader(new UserEntity(data.getXiangmuHeader()));
		c.setJijingHeader(new UserEntity(data.getJijingHeader()));
		c.setStart(data.getStart());
		c.setEnd(data.getEnd());

		// 记录日志
		this.recordOpLog(c, ContractOpLogType.START_CONTRACT, data.getLogRemark());

		// 修复money
		if (data.getMoney() != null)
			for (ContractMoney m : data.getMoney()) {
				if (m == null)
					continue;

				if (m.getTotal() == null)
					continue;

				if (c.getMoney() == null)
					c.setMoney(new ArrayList<ContractMoney>());

				m.setContract(c);

				c.getMoney().add(m);
			}

		c = this.contractSrv.update(c);

		if (c == null)
			return new MessageModel(false, "更信息失败！", null);

		// 更新流程
		FlowOfContract2UserSrv fcus = ApplicationContextHelper.getBeanByType(FlowOfContract2UserSrv.class);
		fcus.sendWithUsers(c.getId(), new String[] {this.getUser().getId()}, FlowStatus.FS2002_CONTRACT_STARTED);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/start-for-a/{id}", method = RequestMethod.GET)
	public String startContractForAForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-start-for-a";

		// 无效的合同信息
		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("id", id);
		model.addAttribute("xmh", this.getUserByRole("" + SystemAtomRoles.HEADER_XIANGMU));
		model.addAttribute("jjh", this.getUserByRole("" + SystemAtomRoles.HEADER_JIJING));

		return view;
	}

	@RequestMapping(value = "/reset-project-time/{id}/{start}/{end}", method = RequestMethod.GET)
	public String resetProjectTimeForm(@PathVariable String id, @PathVariable String start, @PathVariable String end, Model model) {
		String view = "app/contract/contract-view-reset-project-time";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("id", id);
		model.addAttribute("start", start);
		model.addAttribute("end", end);

		return view;
	}

	@RequestMapping(value = "/reset-project-time/{id}", method = RequestMethod.POST)
	@ResponseBody
	public Object resetProjectTime(@PathVariable String id, @DateTimeFormat(pattern = "yyyy-MM-dd") Date start, @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, String logRemark) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效的合同信息！", null);

		Contract c = this.contractSrv.get(id);

		if (c == null)
			return new MessageModel(false, "无法找到对应的合同信息！", null);

		c.setStart(start);
		c.setEnd(end);

		// 记录操作日志
		this.recordOpLog(c, ContractOpLogType.RESET_PROJECT_TIME, logRemark);

		c = this.contractSrv.update(c);

		if (c == null)
			return new MessageModel(false, "更新操作失败！", null);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/update-status-only/{id}/{status}", method = RequestMethod.GET)
	public String updateStatusOnlyForm(@PathVariable String id, @PathVariable ContractStatus status, Model model) {
		String view = "app/contract/contract-update-status-only";

		if (StringUtils.isBlank(id) || status == null) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("id", id);
		model.addAttribute("status", status);

		return view;
	}

	@RequestMapping(value = "/update-status-only/{id}/{status}", method = RequestMethod.POST)
	@ResponseBody
	public Object updateStatusOnly(@PathVariable String id, @PathVariable ContractStatus status, String logRemark) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效的合同信息！", null);

		if (status == null)
			return new MessageModel(false, "状态信息无效！", null);

		Contract c = this.contractSrv.get(id);

		if (c == null)
			return new MessageModel(false, "无法找到对应的合同信息！", null);

		if (StringUtils.isBlank(logRemark))
			logRemark = "";

		logRemark += "【" + c.getStatus().getValue() + "】-->【" + status.getValue() + "】";

		c.setStatus(status);

		// 记录日志
		this.recordOpLog(c, ContractOpLogType.UPDATE_CONTRACT_STATUS, logRemark);

		c = this.contractSrv.update(c);

		if (c == null)
			return new MessageModel(false, "更新状态失败！", null);

		// 终止后需要更改流程为completed状态
		if (ContractStatus.SHUTDOWN.equals(status)) {
			FlowCtrlForProject flow = ApplicationContextHelper.getBeanByType(FlowCtrlForProject.class);
			flow.changeStatusForContract(c.getId(), FlowStatus.FS2003_CONTRACT_COMPLETED);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/pay-in/{id}", method = RequestMethod.GET)
	public String payinForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payin";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		ContractMoney money = this.newPayinMoney(true, null, null);

		money.setContract(new Contract());
		money.getContract().setId(id);

		model.addAttribute("data", money);

		return view;
	}

	@RequestMapping(value = "/pay-in", method = RequestMethod.POST)
	@ResponseBody
	public Object payin(ContractMoney money) {
		if (money == null)
			return new MessageModel(false, "无效的收款信息！", null);

		if (money.getContract() == null || StringUtils.isBlank(money.getContract().getId()))
			return new MessageModel(false, "未能找到对应的合同信息，无法记录收款情况！", null);

		if (money.getTotal() == null || money.getTime() == null)
			return new MessageModel(false, "【收款金额】、【收款日期】不能为空！", null);

		money = this.contractMoneySrv.save(money);

		if (money == null || StringUtils.isBlank(money.getId()))
			return new MessageModel(false, "记录收款情况出错！", null);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(money.getContract(), ContractOpLogType.CONTRACT_PAY_IN, "金额：" + this.numberFormat(money.getTotal()) + "（" + this.datetimeFormat(money.getTime(), "yyyy-MM-dd") + "）");
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/payin-plan/{id}", method = RequestMethod.GET)
	public String payinPlanForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payin-plan";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		Contract c = this.contractSrv.get(id);

		List<UserEntity> header = this.getUserByRole("" + SystemAtomRoles.ZHUANZE_MONEY_IN);

		model.addAttribute("contract", c);
		model.addAttribute("header", header);

		return view;
	}

	@RequestMapping(value = "/payin-plan", method = RequestMethod.POST)
	@ResponseBody
	public Object payinPlan(ContractPayinPlan plan) {
		if (plan == null)
			return new MessageModel(false, "无效的收款信息！", null);

		if (plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
			return new MessageModel(false, "未能找到对应的合同信息，无法记录收款情况！", null);

		if (StringUtils.isBlank(plan.getReason()) || plan.getTotal() == null || plan.getBackTime() == null)
			return new MessageModel(false, "【收款事项】、【收款金额】、【收款日期】不能为空！", null);

		if (plan.getCreateTime() == null)
			plan.setCreateTime(new Date());

		plan.setCreater(new UserEntity(this.getUser().getId()));

		plan = this.contractPayinPlanSrv.save(plan);

		if (plan == null || StringUtils.isBlank(plan.getId()))
			return new MessageModel(false, "记录收款计划出错！", null);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(plan.getContract(), ContractOpLogType.CONTRACT_PAY_IN_PLAN, "金额：" + this.numberFormat(plan.getTotal()) + "（" + this.datetimeFormat(plan.getBackTime(), "yyyy-MM-dd") + "）");
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/payin-plan-multiple/{id}", method = RequestMethod.GET)
	public String payinPlanMultipleForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payin-plan-multiple";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		Contract c = this.contractSrv.get(id);

		List<UserEntity> header = this.getUserByRole("" + SystemAtomRoles.ZHUANZE_MONEY_IN);

		model.addAttribute("contract", c);
		model.addAttribute("header", header);

		return view;
	}

	@RequestMapping(value = "/payin-plan-multiple", method = RequestMethod.POST)
	@ResponseBody
	public Object payinPlanMultiple(ContractPayinPlanArray plans) {
		if (plans == null || plans.getArray() == null || plans.getArray().isEmpty())
			return new MessageModel(false, "无效的收款信息！", null);

		for (ContractPayinPlan plan : plans.getArray()) {
			if (plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
				continue;

			if (StringUtils.isBlank(plan.getReason()) || plan.getTotal() == null || plan.getBackTime() == null)
				continue;

			if (plan.getCreateTime() == null)
				plan.setCreateTime(new Date());

			plan.setCreater(new UserEntity(this.getUser().getId()));

			plan = this.contractPayinPlanSrv.save(plan);

			if (plan == null || StringUtils.isBlank(plan.getId()))
				continue;

			// 记录主合同操作日志
			ContractOpLog log = this.recordOpLog(plan.getContract(), ContractOpLogType.CONTRACT_PAY_IN_PLAN, "金额：" + this.numberFormat(plan.getTotal()) + "（" + this.datetimeFormat(plan.getBackTime(), "yyyy-MM-dd") + "）");
			if (log == null)
				return new MessageModel(false, "记录操作日志出错", null);
			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/payin-plan-change/{id}", method = RequestMethod.GET)
	public String changePayinPlanForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payin-plan-change";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		String ql = "SELECT T, T.contract.id FROM ContractPayinPlan T WHERE T.id=?";

		Object[] obj = (Object[]) this.contractPayinPlanSrv.readSingleByQL(ql, id);

		if (obj == null) {
			model.addAttribute("invalid", true);
			return view;
		}

		ContractPayinPlan data = (ContractPayinPlan) obj[0];
		String contractId = (String) obj[1];

		model.addAttribute("data", data);
		model.addAttribute("cid", contractId);
		model.addAttribute("header", this.getUserByRole("" + SystemAtomRoles.ZHUANZE_MONEY_IN));

		return view;
	}

	@RequestMapping(value = "/payin-plan-change", method = RequestMethod.POST)
	@ResponseBody
	public Object changePayinPlan(ContractPayinPlan plan) {
		if (plan == null || StringUtils.isBlank(plan.getId()) || plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
			return MessageModel.createErrorMessage("无效的收款计划！", null);

		if (StringUtils.isBlank(plan.getChangeReason()))
			return MessageModel.createErrorMessage("请正确填写变更原因！", null);

		ContractPayinPlan data = this.contractPayinPlanSrv.get(plan.getId());

		if (data == null)
			return MessageModel.createErrorMessage("未找到需要变更的收款计划信息！", null);

		String newReason = plan.getChangeReason();

		plan.setChangeReason(this.makeChangeReasonString(data.getChangeReason(), newReason));

		String contractId = plan.getContract().getId();

		plan = this.contractPayinPlanSrv.update(plan);

		if (plan == null)
			return MessageModel.createErrorMessage("变更工期计划失败！", null);

		// 记录主合同操作日志
		Contract c = new Contract();
		c.setId(contractId);
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.CONTRACT_PAY_IN_PLAN_CHANGE, newReason);
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/work-plan/{id}", method = RequestMethod.GET)
	public String workPlanForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-work-plan";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("cid", id);
		model.addAttribute("milestone", WorkPlanMilestone.values());

		return view;
	}

	@RequestMapping(value = "/work-plan", method = RequestMethod.POST)
	@ResponseBody
	public Object workPlan(ContractWorkPlan plan) {
		if (plan == null)
			return new MessageModel(false, "无效的工期计划信息！", null);

		if (plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
			return new MessageModel(false, "未能找到对应的合同信息，无法记录工期计划！", null);

		plan.setCreater(new UserEntity(this.getUser().getId()));

		plan = this.contractWorkPlanSrv.save(plan);

		if (plan == null || StringUtils.isBlank(plan.getId()))
			return new MessageModel(false, "记录工期计划出错！", null);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(plan.getContract(), ContractOpLogType.CONTRACT_WORK_PLAN, "时间节点：" + this.datetimeFormat(plan.getStartDatePoint(), "yyyy-MM-dd") + " 至 " + this.datetimeFormat(plan.getDatePoint(), "yyyy-MM-dd"));
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/work-plan-multiple/{id}", method = RequestMethod.GET)
	public String workPlanMultipleForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-work-plan-multiple";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("cid", id);
		model.addAttribute("milestone", WorkPlanMilestone.values());

		return view;
	}

	@RequestMapping(value = "/work-plan-multiple", method = RequestMethod.POST)
	@ResponseBody
	@Transactional
	public Object workPlanMultiple(ContractWorkPlanArray plans) {
		if (plans == null || plans.getArray() == null || plans.getArray().isEmpty())
			return new MessageModel(false, "无效的工期计划信息！", null);

		for (ContractWorkPlan plan : plans.getArray()) {
			if (plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
				continue;

			plan.setCreater(new UserEntity(this.getUser().getId()));

			plan = this.contractWorkPlanSrv.save(plan);

			if (plan == null || StringUtils.isBlank(plan.getId()))
				continue;

			// 记录主合同操作日志
			ContractOpLog log = this.recordOpLog(plan.getContract(), ContractOpLogType.CONTRACT_WORK_PLAN, "时间节点：" + this.datetimeFormat(plan.getStartDatePoint(), "yyyy-MM-dd") + " 至 " + this.datetimeFormat(plan.getDatePoint(), "yyyy-MM-dd"));
			if (log == null)
				return new MessageModel(false, "记录操作日志出错", null);
			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/work-plan-change", method = RequestMethod.GET)
	public String changeWorkPlanForm(String id, Model model) {
		String view = "app/contract/contract-work-plan-change";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		String ql = "SELECT T, T.contract.id FROM ContractWorkPlan T WHERE T.id=?";

		Object[] obj = (Object[]) this.contractWorkPlanSrv.readSingleByQL(ql, id);

		if (obj == null) {
			model.addAttribute("invalid", true);
			return view;
		}

		ContractWorkPlan data = (ContractWorkPlan) obj[0];
		String contractId = (String) obj[1];

		model.addAttribute("data", data);
		model.addAttribute("milestone", WorkPlanMilestone.values());
		model.addAttribute("cid", contractId);

		return view;
	}

	@RequestMapping(value = "/work-plan-change", method = RequestMethod.POST)
	@ResponseBody
	public Object changeWorkPlan(ContractWorkPlan plan) {
		if (plan == null || StringUtils.isBlank(plan.getId()))
			return MessageModel.createErrorMessage("无效的工期计划！", null);

		if (StringUtils.isBlank(plan.getChangeReason()))
			return MessageModel.createErrorMessage("请正确填写变更原因！", null);

		ContractWorkPlan data = this.contractWorkPlanSrv.get(plan.getId());

		if (data == null)
			return MessageModel.createErrorMessage("未找到需要变更的工期计划信息！", null);

		String newReason = plan.getChangeReason();

		plan.setChangeReason(this.makeChangeReasonString(data.getChangeReason(), newReason));

		String contractId = plan.getContract().getId();

		plan = this.contractWorkPlanSrv.update(plan);

		if (plan == null)
			return MessageModel.createErrorMessage("变更工期计划失败！", null);

		// 记录主合同操作日志
		Contract c = new Contract();
		c.setId(contractId);
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.CONTRACT_WORK_PLAN_CHANGE, newReason);
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return MessageModel.createSuccessMessage("操作成功！", null);
	}

	@RequestMapping(value = "/finish-contract/{id}", method = RequestMethod.GET)
	public String finishContractForm(@PathVariable String id, String end, Model model) {
		String view = "app/contract/contract-finish";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("cid", id);
		model.addAttribute("end", end);

		return view;
	}

	@RequestMapping(value = "/finish-contract/{id}", method = RequestMethod.POST)
	@ResponseBody
	public Object finishContract(@PathVariable String id, @DateTimeFormat(pattern = "yyyy-MM-dd") Date time, String remark, @RequestParam(value = "file", required = false) MultipartFile[] files) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效合同信息，禁止操作！", null);

		if (time == null)
			return new MessageModel(false, "竣工时间必须填写！", null);

		Contract c = this.contractSrv.get(id);

		if (c == null)
			return new MessageModel(false, "未能找到对应的合同信息，操作失败！", null);

		c.setEnd(time);
		c.setStatus(ContractStatus.FINISH);

		c = this.contractFileSrv.update(c);

		if (c == null)
			return new MessageModel(false, "更新实际结束工期失败！", null);

		if (StringUtils.isBlank(remark))
			remark = "";

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.FINISH_CONTRACT, remark + "（竣工日期：" + this.datetimeFormat(time, "yyyy-MM-dd") + "）");
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		// 保存文件
		if (files != null) {
			for (MultipartFile f : files) {
				this.saveFile(f, c.getId(), UserFileType.F201_CONTRACT_COMPLETE_FILE);
			}
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/clearing-contract/{id}", method = RequestMethod.GET)
	public String clearingContract(@PathVariable String id, Model model) {
		String view = "app/contract/contract-clearing";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		Contract c = this.contractSrv.get(id);

		if (c == null || StringUtils.isBlank(c.getId())) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("c", c);

		return view;
	}

	@RequestMapping(value = "/clearing-contract/{id}", method = RequestMethod.POST)
	@ResponseBody
	public Object clearingContract(@PathVariable String id, double clearingPrice, String invoiceStatus, String clearingStatus, String remark) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "操作失败，无效的合同信息！", null);

		Contract c = this.contractSrv.get(id);

		if (c == null || StringUtils.isBlank(c.getId()))
			return new MessageModel(false, "操作失败，未找到对应的合同信息！", null);

		if (StringUtils.isBlank(remark))
			remark = "";

		// 记录日志
		this.recordOpLog(c, ContractOpLogType.COMPLETE_CONTRACT, remark + " 【结算金额】：" + this.numberFormat(clearingPrice));

		c.setStatus(ContractStatus.COMPLETED);
		c.setClearingPrice(clearingPrice);
		c.setInvoiceStatus(invoiceStatus);
		c.setClearingStatus(clearingStatus);

		c = this.contractSrv.update(c);

		if (c == null || StringUtils.isBlank(c.getId()))
			return new MessageModel(false, "操作失败，保存信息失败！", null);

		// 完工后需要修改流程状态
		FlowCtrlForProject flow = ApplicationContextHelper.getBeanByType(FlowCtrlForProject.class);
		flow.changeStatusForContract(c.getId(), FlowStatus.FS2003_CONTRACT_COMPLETED);

		return new MessageModel(true, "操作成功！", null);
	}

	/**
	 * 通过计划收款
	 * 
	 * @param planId 计划id
	 * @param faPiao 发票开具情况
	 * @return
	 */
	@RequestMapping(value = "/get-back-money-by-plan", method = RequestMethod.POST)
	@ResponseBody
	public Object getBackMoneyByPlan(String id, String planId, boolean faPiao) {
		if (StringUtils.isBlank(id))
			return MessageModel.createErrorMessage("操作失败，无效的合同信息！", null);

		if (StringUtils.isBlank(planId))
			return MessageModel.createErrorMessage("操作失败，无效的计划信息！", null);

		ContractPayinPlan plan = this.contractPayinPlanSrv.get(planId);

		if (Boolean.TRUE.equals(plan.getBack()))
			return MessageModel.createSuccessMessage("操作成功！", null);

		if (plan == null || StringUtils.isBlank(plan.getId()))
			return MessageModel.createErrorMessage("操作失败，无法找到计划信息！", null);

		Contract c = new Contract();
		c.setId(id);

		ContractMoney money = new ContractMoney();

		money.setBackTime(null);
		money.setContract(c);
		money.setGaveBack(null);
		money.setId(null);
		money.setInAccount(true);
		money.setInvoice(faPiao);
		money.setName(null);
		money.setPayOut(false);
		money.setRemark(plan.getReason());
		money.setSource(null);
		money.setTime(plan.getBackTime());
		money.setTotal(plan.getTotal());

		money = this.contractMoneySrv.save(money);

		if (money == null)
			return MessageModel.createErrorMessage("操作失败，收款失败！", null);

		plan.setBack(true);
		plan = this.contractPayinPlanSrv.update(plan);

		if (plan == null)
			return MessageModel.createErrorMessage("操作失败，款项已收，但是计划信息未能更新！", null);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.CONTRACT_PAY_IN, money.getRemark() + " 收款金额：" + this.numberFormat(money.getTotal()));
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return MessageModel.createSuccessMessage("操作成功！", null);
	}

	/**
	 * 通过计划付款
	 * 
	 * @param planId 计划id
	 * @param faPiao 发票开具情况
	 * @return
	 */
	@RequestMapping(value = "/payout-money-by-plan", method = RequestMethod.POST)
	@ResponseBody
	public Object payoutMoneyByPlan(String id, String planId, boolean faPiao) {
		if (StringUtils.isBlank(id))
			return MessageModel.createErrorMessage("操作失败，无效的合同信息！", null);

		if (StringUtils.isBlank(planId))
			return MessageModel.createErrorMessage("操作失败，无效的计划信息！", null);

		ContractPayoutPlan plan = this.contractPayoutPlanSrv.get(planId);

		if (Boolean.TRUE.equals(plan.getPayout()))
			return MessageModel.createSuccessMessage("操作成功！", null);

		if (plan == null || StringUtils.isBlank(plan.getId()))
			return MessageModel.createErrorMessage("操作失败，无法找到计划信息！", null);

		Contract c = new Contract();
		c.setId(id);

		ContractMoney money = new ContractMoney();

		money.setBackTime(null);
		money.setContract(c);
		money.setGaveBack(null);
		money.setId(null);
		money.setInAccount(true);
		money.setInvoice(faPiao);
		money.setName(null);
		money.setPayOut(true);
		money.setRemark(plan.getReason());
		money.setSource(null);
		money.setTime(plan.getPayoutTime());
		money.setTotal(plan.getTotal());

		money = this.contractMoneySrv.save(money);

		if (money == null)
			return MessageModel.createErrorMessage("操作失败，付款失败！", null);

		plan.setPayout(true);
		plan = this.contractPayoutPlanSrv.update(plan);

		if (plan == null)
			return MessageModel.createErrorMessage("操作失败，款项已付，但是计划信息未能更新！", null);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.CONTRACT_PAY_OUT, money.getRemark() + " 收款金额：" + this.numberFormat(money.getTotal()));
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return MessageModel.createSuccessMessage("操作成功！", null);
	}

	@RequestMapping(value = "/work-completed-by-plan", method = RequestMethod.POST)
	@ResponseBody
	public Object workCompletedByPlan(String id, String planId) {
		if (StringUtils.isBlank(id))
			return MessageModel.createErrorMessage("操作失败，无效的合同信息！", null);

		if (StringUtils.isBlank(planId))
			return MessageModel.createErrorMessage("操作失败，无效的计划信息！", null);

		ContractWorkPlan plan = this.contractWorkPlanSrv.get(planId);

		if (plan == null || StringUtils.isBlank(plan.getId()))
			return MessageModel.createErrorMessage("操作失败，无法找到计划信息！", null);

		plan.setCompleted(true);
		plan = this.contractWorkPlanSrv.update(plan);

		if (plan == null)
			return MessageModel.createErrorMessage("操作失败，款项已收，但是计划信息未能更新！", null);

		Contract c = new Contract();
		c.setId(id);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.CONTRACT_COMPLETED_WORK_PLAN, "【完成计划】" + plan.getWork());
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return MessageModel.createSuccessMessage("操作成功！", null);
	}

	@RequestMapping(value = "/payout-plan/{id}", method = RequestMethod.GET)
	public String payoutPlanForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payout-plan";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		Contract c = this.contractSrv.get(id);

		List<UserEntity> header = this.getUserByRole("" + SystemAtomRoles.ZHUANZE_MONEY_OUT);

		model.addAttribute("contract", c);
		model.addAttribute("header", header);

		return view;
	}

	@RequestMapping(value = "/payout-plan", method = RequestMethod.POST)
	@ResponseBody
	public Object payoutPlan(ContractPayoutPlan plan) {
		if (plan == null)
			return new MessageModel(false, "无效的付款信息！", null);

		if (plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
			return new MessageModel(false, "未能找到对应的合同信息，无法记录付款情况！", null);

		if (StringUtils.isBlank(plan.getReason()) || plan.getTotal() == null || plan.getPayoutTime() == null)
			return new MessageModel(false, "【付款事项】、【付款金额】、【付款日期】不能为空！", null);

		if (plan.getCreateTime() == null)
			plan.setCreateTime(new Date());

		plan.setCreater(new UserEntity(this.getUser().getId()));

		plan = this.contractPayoutPlanSrv.save(plan);

		if (plan == null || StringUtils.isBlank(plan.getId()))
			return new MessageModel(false, "记录付款计划出错！", null);

		// 记录主合同操作日志
		ContractOpLog log = this.recordOpLog(plan.getContract(), ContractOpLogType.CONTRACT_PAY_OUT_PLAN, "金额：" + this.numberFormat(plan.getTotal()) + "（" + this.datetimeFormat(plan.getPayoutTime(), "yyyy-MM-dd") + "）");
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/payout-plan-multiple/{id}", method = RequestMethod.GET)
	public String payoutPlanMultipleForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payout-plan-multiple";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		Contract c = this.contractSrv.get(id);

		List<UserEntity> header = this.getUserByRole("" + SystemAtomRoles.ZHUANZE_MONEY_OUT);

		model.addAttribute("contract", c);
		model.addAttribute("header", header);

		return view;
	}

	@RequestMapping(value = "/payout-plan-multiple", method = RequestMethod.POST)
	@ResponseBody
	public Object payoutPlanMultiple(ContractPayoutPlanArray plans) {
		if (plans == null || plans.getArray() == null || plans.getArray().isEmpty())
			return new MessageModel(false, "无效的付款信息！", null);

		for (ContractPayoutPlan plan : plans.getArray()) {
			if (plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
				continue;

			if (StringUtils.isBlank(plan.getReason()) || plan.getTotal() == null || plan.getPayoutTime() == null)
				continue;

			if (plan.getCreateTime() == null)
				plan.setCreateTime(new Date());

			plan.setCreater(new UserEntity(this.getUser().getId()));

			plan = this.contractPayoutPlanSrv.save(plan);

			if (plan == null || StringUtils.isBlank(plan.getId()))
				continue;

			// 记录主合同操作日志
			ContractOpLog log = this.recordOpLog(plan.getContract(), ContractOpLogType.CONTRACT_PAY_OUT_PLAN, "金额：" + this.numberFormat(plan.getTotal()) + "（" + this.datetimeFormat(plan.getPayoutTime(), "yyyy-MM-dd") + "）");
			if (log == null)
				return new MessageModel(false, "记录操作日志出错", null);
			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/payout-plan-change/{id}", method = RequestMethod.GET)
	public String changePayoutPlanForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-payout-plan-change";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		String ql = "SELECT T, T.contract.id FROM ContractPayoutPlan T WHERE T.id=?";

		Object[] obj = (Object[]) this.contractPayoutPlanSrv.readSingleByQL(ql, id);

		if (obj == null) {
			model.addAttribute("invalid", true);
			return view;
		}

		ContractPayoutPlan data = (ContractPayoutPlan) obj[0];
		String contractId = (String) obj[1];

		model.addAttribute("data", data);
		model.addAttribute("cid", contractId);
		model.addAttribute("header", this.getUserByRole("" + SystemAtomRoles.ZHUANZE_MONEY_OUT));

		return view;
	}

	@RequestMapping(value = "/payout-plan-change", method = RequestMethod.POST)
	@ResponseBody
	public Object changePayoutPlan(ContractPayoutPlan plan) {
		if (plan == null || StringUtils.isBlank(plan.getId()) || plan.getContract() == null || StringUtils.isBlank(plan.getContract().getId()))
			return MessageModel.createErrorMessage("无效的付款计划！", null);

		if (StringUtils.isBlank(plan.getChangeReason()))
			return MessageModel.createErrorMessage("请正确填写变更原因！", null);

		ContractPayoutPlan data = this.contractPayoutPlanSrv.get(plan.getId());

		if (data == null)
			return MessageModel.createErrorMessage("未找到需要变更的付款计划信息！", null);

		String newReason = plan.getChangeReason();

		plan.setChangeReason(this.makeChangeReasonString(data.getChangeReason(), newReason));

		String contractId = plan.getContract().getId();

		plan = this.contractPayoutPlanSrv.update(plan);

		if (plan == null)
			return MessageModel.createErrorMessage("变更工期计划失败！", null);

		// 记录主合同操作日志
		Contract c = new Contract();
		c.setId(contractId);
		ContractOpLog log = this.recordOpLog(c, ContractOpLogType.CONTRACT_PAY_OUT_PLAN_CHANGE, newReason);
		if (log == null)
			return new MessageModel(false, "记录操作日志出错", null);
		this.contractOpLogSrv.save(log);

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
	@ResponseBody
	@Transactional
	public Object deleteSubContract(@PathVariable String id) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效的合同信息！", null);

		Contract data = this.contractSrv.get(id);

		if (data == null)
			return new MessageModel(false, "无法找到指定合同！", null);

		String parentId = null;
		if (data.getParent() != null)
			parentId = this.contractSrv.getParentId(id);

		data = this.contractSrv.delete(data);

		if (data == null)
			return new MessageModel(false, "删除合同失败！", null);

		// 将子合同的删除信息记录在父合同中
		if (!StringUtils.isBlank(parentId)) {
			Contract parent = new Contract();
			parent.setId(parentId);
			// 记录日志
			ContractOpLog log = this.recordOpLog(parent, ContractOpLogType.CONTRACT_DELETE, "删除合同[" + data.getName() + "]");

			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/delete-work-plan/{id}", method = RequestMethod.POST)
	@ResponseBody
	@Transactional
	public Object deleteWorkPlan(@PathVariable String id) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效的工期计划信息！", null);

		ContractWorkPlan data = this.contractWorkPlanSrv.get(id);

		if (data == null)
			return new MessageModel(false, "无法找到指定工程计划！", null);

		String contractId = null;
		if (data.getContract() != null)
			contractId = this.contractWorkPlanSrv.getContractId(id);

		data = this.contractWorkPlanSrv.delete(data);

		if (data == null)
			return new MessageModel(false, "删除工程计划失败！", null);

		// 将子合同的删除信息记录在父合同中
		if (!StringUtils.isBlank(contractId)) {
			Contract parent = new Contract();
			parent.setId(contractId);

			String remark = data.getWork();
			remark += " 计划完成时间：" + this.datetimeFormat(data.getDatePoint(), "yyyy-MM-dd");

			// 记录日志
			ContractOpLog log = this.recordOpLog(parent, ContractOpLogType.CONTRACT_WORK_PLAN_DELETE, remark);

			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/delete-payin-plan/{id}", method = RequestMethod.POST)
	@ResponseBody
	public Object deletePayinPlan(@PathVariable String id) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效的收款计划信息！", null);

		ContractPayinPlan data = this.contractPayinPlanSrv.get(id);

		if (data == null)
			return new MessageModel(false, "无法找到指定收款计划！", null);

		String contractId = null;
		if (data.getContract() != null)
			contractId = this.contractPayinPlanSrv.getContractId(id);

		data = this.contractPayinPlanSrv.delete(data);

		if (data == null)
			return new MessageModel(false, "删除收款计划失败！", null);

		// 将子合同的删除信息记录在父合同中
		if (!StringUtils.isBlank(contractId)) {
			Contract parent = new Contract();
			parent.setId(contractId);

			String remark = data.getReason();
			remark += "[" + this.numberFormat(data.getTotal()) + "]";
			remark += " 计划收款时间：" + this.datetimeFormat(data.getBackTime(), "yyyy-MM-dd");

			// 记录日志
			ContractOpLog log = this.recordOpLog(parent, ContractOpLogType.CONTRACT_PAYIN_PLAN_DELETE, remark);

			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/delete-payout-plan/{id}", method = RequestMethod.POST)
	@ResponseBody
	public Object deletePayoutPlan(@PathVariable String id) {
		if (StringUtils.isBlank(id))
			return new MessageModel(false, "无效的付款计划信息！", null);

		ContractPayoutPlan data = this.contractPayoutPlanSrv.get(id);

		if (data == null)
			return new MessageModel(false, "无法找到指定付款计划！", null);

		String contractId = null;
		if (data.getContract() != null)
			contractId = this.contractPayoutPlanSrv.getContractId(id);

		data = this.contractPayoutPlanSrv.delete(data);

		if (data == null)
			return new MessageModel(false, "删除付款计划失败！", null);

		// 将子合同的删除信息记录在父合同中
		if (!StringUtils.isBlank(contractId)) {
			Contract parent = new Contract();
			parent.setId(contractId);

			String remark = data.getReason();
			remark += "[" + this.numberFormat(data.getTotal()) + "]";
			remark += " 计划付款时间：" + this.datetimeFormat(data.getPayoutTime(), "yyyy-MM-dd");

			// 记录日志
			ContractOpLog log = this.recordOpLog(parent, ContractOpLogType.CONTRACT_PAYOUT_PLAN_DELETE, remark);

			this.contractOpLogSrv.save(log);
		}

		return new MessageModel(true, "操作成功！", null);
	}

	@RequestMapping(value = "/cost-money/{id}", method = RequestMethod.GET)
	public String costMoneyForm(@PathVariable String id, Model model) {
		String view = "app/contract/contract-cost-money";

		if (StringUtils.isBlank(id)) {
			model.addAttribute("invalid", true);
			return view;
		}

		model.addAttribute("cid", id);
		model.addAttribute("payoutNames", this.constants.getBidPayoutNames(null));

		return view;
	}

	@RequestMapping(value = "/cost-money", method = RequestMethod.POST)
	@ResponseBody
	public Object costMoney(ContractMoney money) {
		if (money == null || StringUtils.isBlank(money.getContract().getId()))
			return MessageModel.createErrorMessage("资金信息无效！", null);

		money = this.contractMoneySrv.save(money);

		if (money == null || StringUtils.isBlank(money.getId()))
			return MessageModel.createErrorMessage("保存资金信息失败！", null);

		return MessageModel.createSuccessMessage("操作成功！", null);
	}

	@RequestMapping(value = "/back-contract", method = RequestMethod.POST)
	@ResponseBody
	public Object backContract(String id) {
		if (StringUtils.isBlank(id))
			return MessageModel.createErrorMessage("无效的合同信息，无法退回！", null);

		Contract data = this.contractSrv.get(id);

		if (data == null)
			return MessageModel.createErrorMessage("无法找到需要退回的合同信息！", null);

		data.setStatus(ContractStatus.WAIT);// 标记合同为待建
		data.setFlowStatus(FlowStatus.FS2000_CONTRACT_TO_CREATE);// 标记合同为合同准备中

		for (int i = data.getFlowInfo().size() - 1; i >= 0; i--) {
			FlowOfContract2User fu = data.getFlowInfo().get(i);

			if (fu == null)
				data.getFlowInfo().remove(i);

			FlowStatus s = fu.getStatus();

			// 合同准备中
			if (FlowStatus.FS2000_CONTRACT_TO_CREATE.equals(s)) {
				fu.setActive(true);

				continue;
			}

			data.getFlowInfo().remove(i);
		}

		data = this.contractSrv.update(data);

		if (data == null || StringUtils.isBlank(data.getId()))
			return MessageModel.createErrorMessage("退回操作失败！", null);

		return MessageModel.createSuccessMessage("操作成功！", null);
	}

	private Contract newContract(Bid bid, ContractType type) {
		Contract c = new Contract();

		if (bid == null) {
			bid = new Bid();
			bid.setBiddingerInfo(new BiddingerInfo());
			bid.getBiddingerInfo().setBiddinger(new Biddinger());
		}

		c.setName(bid.getName());
		c.setPrice(bid.getLastResult().getFinalMoney());
		c.setCompanyA(bid.getBiddingerInfo().getBiddinger().getCompany());
		c.setType(type);
		c.setStatus(ContractStatus.WAIT);
		c.setCompanyB(bid.getOwnCompany());

		if (!StringUtils.isBlank(bid.getId()))
			c.setBid(bid);

		return c;
	}

	private boolean saveFile(MultipartFile file, String contractId, UserFileType type) {
		if (StringUtils.isBlank(contractId))
			return false;

		if (type == null)
			type = UserFileType.Z900_OTHER;

		Contract c = new Contract();
		c.setId(contractId);

		String fileName = file.getOriginalFilename();

		String suffix = fileName.substring(fileName.lastIndexOf('.'));
		fileName = fileName.substring(0, fileName.lastIndexOf('.'));

		try {
			File tmp = this.fileTool.store(file.getInputStream(), contractId);

			ContractFile data = new ContractFile();

			data.setType(type);
			data.setContract(c);
			data.setFileName(fileName);
			data.setFilePath(this.fileTool.relativelyPath(tmp));
			data.setSize(file.getSize());
			data.setSuffix(suffix);
			data.setUploadTime(new Date());
			data.setUserName(this.getUser().getName());

			data = this.contractFileSrv.save(data);

			return data != null && !StringUtils.isBlank(data.getId());
		} catch (IOException e) {
			log.error("Receive file error", e);
		}

		return false;
	}

	/**
	 * 新建操作日志
	 * 
	 * @param op
	 * @param remark
	 * @return
	 */
	private ContractOpLog newOpLog(ContractOpLogType op, String remark) {
		if (op == null)
			return null;

		ContractOpLog log = new ContractOpLog();

		log.setTime(new Date());
		log.setOp(op);
		log.setOper(this.getUser().getName());
		log.setRemark(remark);

		return log;
	}

	/**
	 * 记录操作日志
	 * 
	 * @param c
	 * @param op
	 * @param remark
	 */
	private ContractOpLog recordOpLog(Contract c, ContractOpLogType op, String remark) {
		if (c == null)
			return null;

		ContractOpLog log = this.newOpLog(op, remark);

		if (log == null)
			return null;

		if (c.getOpLog() == null)
			c.setOpLog(new ArrayList<ContractOpLog>());

		log.setContract(c);

		c.getOpLog().add(log);

		return log;
	}

	private ContractMoney newPayinMoney(boolean inAccount, Boolean gaveBack, String name) {
		ContractMoney money = new ContractMoney();

		money.setPayOut(false);
		money.setGaveBack(gaveBack);
		money.setInAccount(inAccount);
		money.setName(name);

		return money;
	}

	private String numberFormat(double number) {
		NumberFormat nf = new DecimalFormat("￥#,###.00");

		return nf.format(number);
	}

	private String datetimeFormat(Date d, String pattern) {
		if (d == null)
			return "";

		SimpleDateFormat sdf = new SimpleDateFormat(pattern);

		return sdf.format(d);
	}

	private List<Biddinger> getBiddingers() {
		Biddinger search = new Biddinger();
		search.setOwner(new OrgEntity());
		search.getOwner().setId(this.getUser().getOrgId());

		List<Biddinger> data = this.biddingerSrv.readByEntity(search);

		return data == null || data.isEmpty() ? null : data;
	}

	/**
	 * 构建工期计划变更信息的字符串
	 * 
	 * @param old
	 * @param now
	 * @return
	 */
	private String makeChangeReasonString(String old, String now) {
		old = StringUtils.isBlank(old) ? "" : old + "++--++";

		return old + this.datetimeFormat(new Date(), "yyyy-MM-dd HH:mm:ss") + "--++--" + this.getUser().getName() + "--++--" + now;
	}

}
